using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using Helper;

namespace SpaceWolf.Graphics
{
    public class LineManager2D
    {
        #region Variables
        /// <summary>
        /// Number of lines used this frame, will be set to 0 when rendering.
        /// </summary>
        private int numOfLines = 0;
        
        /// <summary>
        /// Number of points used this frame, will be set to 0 when rendering.
        /// </summary>
        private int iNumOfPoints = 0;
        
        /// <summary>
        /// Max. number of lines allowed.
        /// </summary>
        private const int MaxNumOfLines = 512;

        /// <summary>
        /// Max. number of points allowed.
        /// </summary>
        private const int MaxNumOfPoints = 512;

        /// <summary>
        /// Real number of primitives currently used.
        /// </summary>
        private int numOfPrimitives = 0;

        /// <summary>
        /// Vertex buffer for all lines
        /// </summary>
        VertexPositionColor[] lineVertices =
            new VertexPositionColor[MaxNumOfLines * 2];

        VertexPositionColor[] pointVerticies =
            new VertexPositionColor[50];

        /// <summary>
        /// The actual list for all the lines, it will NOT be reseted each
        /// frame like numOfLines! We will remember the last lines and
        /// only change this list when anything changes (new line, old
        /// line missing, changing line data).
        /// When this happens buildVertexBuffer will be set to true.
        /// </summary>
        private List<Line> lines = new List<Line>();

        /// <summary>
        /// The actual list for all the lines, it will NOT be reseted each
        /// frame like numOfLines! We will remember the last lines and
        /// only change this list when anything changes (new line, old
        /// line missing, changing line data).
        /// When this happens buildVertexBuffer will be set to true.
        /// </summary>
        private List<Pixel> points = new List<Pixel>();

        /// <summary>
        /// Struct for points 
        /// </summary>
        struct Pixel
        {
            #region Variables
            /// <summary>
            /// Point
            /// </summary>
            public Point point;
            /// <summary>
            /// Color
            /// </summary>
            public Color color;
            #endregion

            #region Constructor
            /// <summary>
            /// Create color point
            /// </summary>
            /// <param name="point">point place</param>
            /// <param name="color">color of point</param>
            public Pixel(Point point, Color color)
            {
                this.point = point;
                this.color = color;
            }// Dot
            #endregion

            #region Equals operators
            /// <summary>
            /// Are these two Lines equal?
            /// </summary>
            public static bool operator ==(Pixel a, Pixel b)
            {
                return a.point== b.point&&
                    a.color == b.color;
            } // ==(a, b)

            /// <summary>
            /// Are these two Lines not equal?
            /// </summary>
            public static bool operator !=(Pixel a, Pixel b)
            {
                return a.point!= b.point ||
                    a.color != b.color;
            } // !=(a, b)

            /// <summary>
            /// Support Equals(.) to keep the compiler happy
            /// (because we used == and !=)
            /// </summary>
            public override bool Equals(object a)
            {
                if (a.GetType() == typeof(Pixel))
                    return (Pixel)a == this;
                else
                    return false; // Object is not a Line
            } // Equals(a)

            /// <summary>
            /// Support GetHashCode() to keep the compiler happy
            /// (because we used == and !=)
            /// </summary>
            public override int GetHashCode()
            {
                return 0; // Not supported or nessescary
            } // GetHashCode()
            #endregion
        }

        /// <summary>
        /// Struct for a line, instances of this struct will be added to lines.
        /// </summary>
        struct Line
        {
            #region Variables
            /// <summary>
            /// Positions
            /// </summary>
            public Point startPoint, endPoint;
            /// <summary>
            /// Color
            /// </summary>
            public Color color;
            #endregion

            #region Constructor
            /// <summary>
            /// Create line
            /// </summary>
            /// <param name="setStartPoint">Set start point</param>
            /// <param name="setEndPoint">Set end point</param>
            /// <param name="setColor">Set color</param>
            public Line(Point setStartPoint,
                Point setEndPoint, Color setColor)
            {
                startPoint = setStartPoint;
                endPoint = setEndPoint;
                color = setColor;
            } // Line(setStartPoint, setEndPoint, setColor)
            #endregion

            #region Equals operators
            /// <summary>
            /// Are these two Lines equal?
            /// </summary>
            public static bool operator ==(Line a, Line b)
            {
                return a.startPoint == b.startPoint &&
                    a.endPoint == b.endPoint &&
                    a.color == b.color;
            } // ==(a, b)

            /// <summary>
            /// Are these two Lines not equal?
            /// </summary>
            public static bool operator !=(Line a, Line b)
            {
                return a.startPoint != b.startPoint ||
                    a.endPoint != b.endPoint ||
                    a.color != b.color;
            } // !=(a, b)

            /// <summary>
            /// Support Equals(.) to keep the compiler happy
            /// (because we used == and !=)
            /// </summary>
            public override bool Equals(object a)
            {
                if (a.GetType() == typeof(Line))
                    return (Line)a == this;
                else
                    return false; // Object is not a Line
            } // Equals(a)

            /// <summary>
            /// Support GetHashCode() to keep the compiler happy
            /// (because we used == and !=)
            /// </summary>
            public override int GetHashCode()
            {
                return 0; // Not supported or nessescary
            } // GetHashCode()
            #endregion
        } // struct Line

        /// <summary>
        /// Vertex declaration for our lines.
        /// </summary>
        VertexDeclaration decl = null;

        /// <summary>
        /// Build vertex buffer this frame because the line list was changed?
        /// Note: The vertex buffer implementation was removed some time ago,
        /// but this variable is still used to check for updates to lineVertices!
        /// </summary>
        private bool buildVertexBuffer = false;
    #endregion

        public LineManager2D()
        {
            decl = new VertexDeclaration(SpaceWolf.Elite.Device, VertexPositionColor.VertexElements);
        }

        public void AddLine(Point startPoint, Point endPoint, Color color)
		{
			// Don't add new lines if limit is reached
			if (numOfLines >= MaxNumOfLines)
			{
				Log.Write("Too many lines requested in LineManager2D. " +
					"Max lines = " + MaxNumOfLines);
				return;
			} // if (numOfLines)

			// Build line
			Line line = new Line(startPoint, endPoint, color);

			// Check if this exact line exists at the current lines position.
			if (lines.Count > numOfLines)
			{
				if ((Line)lines[numOfLines] != line)
				{
					// overwrite old line, otherwise just increase numOfLines
					lines[numOfLines] = line;
					// Remember to build vertex buffer in Render()
					buildVertexBuffer = true;
				} // if (Line)
			} // if (lines.Count)
			else
			{
				// Then just add new line
				lines.Add(line);
				// Remember to build vertex buffer in Render()
				buildVertexBuffer = true;
			} // else

			// nextUpValue line
			numOfLines++;
		} // AddLine(startPoint, endPoint, color)


		/// <summary>
		/// Add line with shadow
		/// </summary>
		/// <param name="startPoint">Start point</param>
		/// <param name="endPoint">End point</param>
		/// <param name="color">Color</param>
		public void AddLineWithShadow(Point startPoint, Point endPoint,
			Color color)
		{
			AddLine(new Point(startPoint.X, startPoint.Y+1),
				new Point(endPoint.X, endPoint.Y+1), Color.Black);
			AddLine(startPoint, endPoint, color);
		} // AddLineWithShadow(startPoint, endPoint, color)

        public void AddPoint(Point point,Color color)
        {
            if (iNumOfPoints >= MaxNumOfPoints)
            {
                Log.Write("Too many points requested in LineManager2D. " +
                    "Max points = " + MaxNumOfPoints);
                return;
            }// if iNumOfPoints 

            // Build point
            Pixel pixel = new Pixel(point, color);

            if (points.Count > iNumOfPoints)
            {
                if ((Pixel)points[iNumOfPoints] != pixel)
                {
                    // overwrite old point, otherwise just increase numOfPoints
                    points[iNumOfPoints] = pixel;
                    // Remember to build vertex buffer in Render()
                    buildVertexBuffer = true;
                } // if (Line)
            }// if(points.count)
            else
            {
                // Then just add new pixel
                points.Add(pixel);
                // Remember to build vertex buffer in Render()
                buildVertexBuffer = true;
            } // else

            // nextUpValue line
            iNumOfPoints++;
        }//AddPoint

        #region Vertex buffer stuff
		/// <summary>
		/// Update vertex buffer
		/// </summary>
        private void UpdateVertexBuffer()
        {
            // Don't do anything if we got no lines.
            if ((numOfLines == 0 ||
                // Or if some data is invalid
                lines.Count < numOfLines) &&
                (iNumOfPoints==0 ||
                points.Count<iNumOfPoints))
            {
                numOfPrimitives = 0;
                return;
            } // if (numOfLines)
            // Set all lines
            for (int lineNum = 0; lineNum < numOfLines; lineNum++)
            {
                Line line = (Line)lines[lineNum];
                lineVertices[lineNum * 2 + 0] = new VertexPositionColor(
                    new Vector3(
                    -1.0f + 2.0f * line.startPoint.X / SpaceWolf.BaseGame.Width,
                    -(-1.0f + 2.0f * line.startPoint.Y / SpaceWolf.BaseGame.Height), 0),
                    line.color);
                lineVertices[lineNum * 2 + 1] = new VertexPositionColor(
                    new Vector3(
                    -1.0f + 2.0f * line.endPoint.X / SpaceWolf.BaseGame.Width,
                    -(-1.0f + 2.0f * line.endPoint.Y / SpaceWolf.BaseGame.Height), 0),
                    line.color);
            } // for (lineNum)
            numOfPrimitives += numOfLines;

            //Set all points
            for(int pixelNum = 0; pixelNum < iNumOfPoints; pixelNum++)
            {
                Pixel pixel = (Pixel)points[pixelNum];
                pointVerticies[pixelNum] = new VertexPositionColor(new Vector3(-1.0f + 2.0f * pixel.point.X / SpaceWolf.BaseGame.Width,
                    -(-1.0f + 2.0f * pixel.point.Y/ SpaceWolf.BaseGame.Height),0.0f), pixel.color);
            }
            numOfPrimitives += iNumOfPoints;
            // Vertex buffer was build
            buildVertexBuffer = false;
        } // UpdateVertexBuffer()
        #endregion

        #region Render
        /// <summary>
        /// Render all lines and pixels added this frame
        /// </summary>
        public virtual void Render()
        {
            // Need to build vertex buffer?
            if (buildVertexBuffer ||
                numOfPrimitives != (numOfLines + iNumOfPoints))
            {
                UpdateVertexBuffer();
            } // if (buildVertexBuffer)

            // Render lines if we got any lines to render
            if (numOfLines > 0)
            {
                //SpaceWolf.Game.BaseGame.AlphaBlending = true;
                //SpaceWolf.Game.BaseGame.WorldMatrix = Matrix.Identity;
                Shaders.ShaderEffect.lineRendering.Render(
                    "LineRendering2D",
                    delegate
                    {
                        SpaceWolf.BaseGame.Device.VertexDeclaration = decl;
                        SpaceWolf.BaseGame.Device.DrawUserPrimitives<VertexPositionColor>(
                            PrimitiveType.LineList, lineVertices, 0, numOfLines);
                    });
            } // if (numOfVertices)

            if (iNumOfPoints > 0)
            {
                //SpaceWolf.Game.BaseGame.Device.RenderState.PointSize = 10f;
                //SpaceWolf.Game.BaseGame.Device.RenderState.AlphaBlendEnable = true;
                //SpaceWolf.Game.BaseGame.WorldMatrix = Matrix.Identity;
                Shaders.ShaderEffect.PixelRendering.Render(
                    "PixelRendering",
                    delegate
                    {
                        SpaceWolf.BaseGame.Device.VertexDeclaration = decl;
                        SpaceWolf.BaseGame.Device.DrawUserPrimitives<VertexPositionColor>(
                            PrimitiveType.PointList, pointVerticies, 0, iNumOfPoints);
                    });
            } // if (numOfVertices)
            // Ok, finally reset numOfLines for next frame
            numOfLines = 0;
            iNumOfPoints = 0;
        } // Render()
        #endregion

        #region Test
        #region TestDraw2DLines
        /// <summary>
        /// Test draw 3D line
        /// </summary>
        //[Test]
        public static void TestDraw2DLine()
        {
            TestGame.Start(
                delegate // 3d render code
                {
                    //for (int num = 0; num < 200; num++)
                    //{
                    //    BaseGame.DrawLine(
                    //        new Vector3(3.0f + num / 10.0f, 3.0f, 0),
                    //        new Vector3(-3.0f + num / 10.0f, -3.0f, 0),
                    //        new Color((byte)(255 - num), 14, (byte)num));
                    //} // for
                    BaseGame.DrawLine(new Point(0,0), new Point(100, 100), Color.White);
                    Fonts.WriteText(2, 30,
                        "cam pos=" + BaseGame.CameraPos);
                });
        } // TestDraw3DLine()
   #endregion        
#endregion

    }
}
